/*
    This file is part of Emu.

    Emu is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Emu is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Emu.  If not, see <http://www.gnu.org/licenses/>. */
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QIcon>
#include <QHeaderView>

#include "ealiaseditor.h"
#include "emacroeditor.h"
#include "etriggereditor.h"
#include "elistpropeditor.h"
#include "esessutils.h"

/* This class handles properties that can be represented inside of a list that
   can grow/shrink as the user likes. It currently handles aliases, macros,
   and triggers, because there is a lot of common code between them. */

#define MOVE_UP   0x1
#define MOVE_DOWN 0x2

EListPropEditor::EListPropEditor(EProfile *profile, EPropType propType)
{
    _profile = profile;
    _propType = propType;

    QVBoxLayout *layout = new QVBoxLayout;
    QHBoxLayout *buttonLayout = new QHBoxLayout;

    _managerTree = new QTreeWidget;
    _managerTree->setUniformRowHeights(true);
    _managerTree->setAllColumnsShowFocus(true);
    _managerTree->setColumnCount(2);
    _managerTree->headerItem()->setHidden(true);

    int i;
    for (i = 0;i != 2;i++)
        _managerTree->header()->setSectionResizeMode(i, QHeaderView::ResizeToContents);

    _addButton = new QPushButton(QIcon(":/icons/add_item.png"), tr("Add"));
    _deleteButton = new QPushButton(QIcon(":/icons/delete_item.png"),
                                    tr("Delete"));
    _downButton = new QPushButton(QIcon(":/icons/down.png"), tr("Down"));
    _editButton = new QPushButton(QIcon(":/icons/edit_item.png"), tr("Edit"));
    _upButton = new QPushButton(QIcon(":/icons/up.png"), tr("Up"));

    buttonLayout->addWidget(_upButton);
    buttonLayout->addWidget(_downButton);
    buttonLayout->insertStretch(2);
    buttonLayout->addWidget(_addButton);
    buttonLayout->addWidget(_deleteButton);
    buttonLayout->insertStretch(5);
    buttonLayout->addWidget(_editButton);

    layout->addWidget(_managerTree);
    layout->addLayout(buttonLayout);
    setLayout(layout);

    /* Nothing is selected by default, so shut the editing off. */
    setEditingEnabled(false);
    setDirectionsEnabled(0);

    connect(_managerTree,
            SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
            this, SLOT(itemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));

    if (propType == EPropAliases) {
        _aliases = _profile->aliases();
        for (i = 0;i < _aliases.size();i++)
            createItemForAlias(_aliases.at(i));
    }
    else if (propType == EPropMacros) {
        _macros = _profile->macros();
        for (i = 0;i < _macros.size();i++)
            createItemForMacro(_macros.at(i));
    }
    else if (propType == EPropTriggers) {
        _triggers = _profile->triggers();
        for (i = 0;i < _triggers.size();i++)
            createItemForTrigger(_triggers.at(i));
    }

    connect(_addButton, SIGNAL(clicked()), this, SLOT(addItem()));
    connect(_editButton, SIGNAL(clicked()), this, SLOT(editItem()));
    connect(_deleteButton, SIGNAL(clicked()), this, SLOT(deleteItem()));
    connect(_downButton, SIGNAL(clicked()), this, SLOT(itemDown()));
    connect(_upButton, SIGNAL(clicked()), this, SLOT(itemUp()));
}

void EListPropEditor::baseDeleteItem()
{
    int index = _currentItemIndex;

    delete _managerTree->takeTopLevelItem(_currentItemIndex);

    /* Use the same index if it's still valid. */
    if (index != _managerTree->topLevelItemCount())
        _currentItemIndex = index;
    else
        /* The last item was deleted, so pick the one above it. */
        _currentItemIndex = index - 1;

    updateDirectionsEnabled();
}

void EListPropEditor::baseMoveItem(int direction)
{
    QTreeWidgetItem *temp;
    int index = _currentItemIndex;

    temp = _managerTree->takeTopLevelItem(index);

    /* It's important to NOT use _currentItemIndex, because it gets changed due
       to the current item being changed.
       Since the inserted item is never the current one, currentItemChanged gets
       fired and fixes _currentItemIndex; */
    if (direction == -1)
        _managerTree->insertTopLevelItem(index - 1, temp);
    else
        _managerTree->insertTopLevelItem(index + 1, temp);

    _managerTree->setCurrentItem(temp);
    temp->setExpanded(true);
}

void EListPropEditor::itemChanged(QTreeWidgetItem *curr, QTreeWidgetItem *prev)
{
    Q_UNUSED(prev);

    if (curr != NULL) {
        /* If there was no selection before, then the editing buttons are
           disabled. Fix that. */
        if (_currentItemIndex == -1)
            setEditingEnabled(true);
        _currentItemIndex = _managerTree->indexOfTopLevelItem(curr);
        updateDirectionsEnabled();
    }
    else {
        /* Don't allow editing of nothing. */
        setEditingEnabled(false);
        setDirectionsEnabled(0);
    }
}

void EListPropEditor::setDirectionsEnabled(int flags)
{
    _downButton->setEnabled(flags & MOVE_DOWN);
    _upButton->setEnabled(flags & MOVE_UP);
}

void EListPropEditor::setEditingEnabled(bool enable)
{
    _deleteButton->setEnabled(enable);
    _editButton->setEnabled(enable);
    _currentItemIndex = -1;
}

void EListPropEditor::updateDirectionsEnabled()
{
    int flags = 0;

    if (_managerTree->topLevelItemCount() >= 2) {
        if (_currentItemIndex != 0)
            flags |= MOVE_UP;

        /* Indexes start from 0 instead of 1. */
        if ((_currentItemIndex + 1) != _managerTree->topLevelItemCount())
            flags |= MOVE_DOWN;
    }

    setDirectionsEnabled(flags);
}


/** Convenience functions for tree manipulation **/
QTreeWidgetItem *EListPropEditor::createChildItem(QString title, QString text)
{
    QTreeWidgetItem *childItem = new QTreeWidgetItem;
    childItem->setFlags(childItem->flags() &
            ~(Qt::ItemIsEditable | Qt::ItemIsEnabled));
    childItem->setText(0, title);
    childItem->setText(1, text);
    return childItem;
}

QTreeWidgetItem *EListPropEditor::createCheckChildItem(QString title)
{
    QTreeWidgetItem *childItem = new QTreeWidgetItem;
    childItem->setFlags(childItem->flags() &
            ~(Qt::ItemIsEditable | Qt::ItemIsEnabled));
    QCheckBox *box = new QCheckBox;
    /// warning: This may not work because the child has not been added.
    _managerTree->setItemWidget(childItem, 1, box);
    childItem->setText(0, title);
    return childItem;
}


/** Alias create and update **/
void EListPropEditor::createItemForAlias(EAlias alias)
{
    QTreeWidgetItem *topItem = new QTreeWidgetItem;
    topItem->setText(0, tr("Name: %1").arg(alias.name()));

    QTreeWidgetItem *inputItem = createChildItem(tr("Input"), alias.input());
    topItem->addChild(inputItem);

    QTreeWidgetItem *outputItem = createChildItem(tr("Output"), alias.output());
    topItem->addChild(outputItem);

    _managerTree->addTopLevelItem(topItem);
    topItem->setExpanded(true);
}

void EListPropEditor::updateItemForAlias(QTreeWidgetItem *item, EAlias alias)
{
    QTreeWidgetItem *inputItem = item->child(0);
    QTreeWidgetItem *outputItem = item->child(1);

    item->setText(0, tr("Name: %1").arg(alias.name()));
    inputItem->setText(1, alias.input());
    outputItem->setText(1, alias.output());
}


/** Macro create and update **/
void EListPropEditor::createItemForMacro(EMacro macro)
{
    QTreeWidgetItem *topItem = new QTreeWidgetItem;
    topItem->setText(0, tr("Name: %1").arg(macro.name()));

    QTreeWidgetItem *pressItem = createChildItem(tr("Keypress: "),
            ::textFromPress(macro.press()));
    topItem->addChild(pressItem);

    QString actionStr;
    if (macro.to() == false)
        actionStr = tr("Send to Server");
    else
        actionStr = tr("Insert into Input");
    QTreeWidgetItem *actionItem = createChildItem(tr("Action: "), actionStr);
    topItem->addChild(actionItem);

    QString dataStr = macro.data().replace("\n", "\\n");
    QTreeWidgetItem *dataItem = createChildItem(tr("Data: "), dataStr);
    topItem->addChild(dataItem);

    _managerTree->addTopLevelItem(topItem);
    topItem->setExpanded(true);
}

void EListPropEditor::updateItemForMacro(QTreeWidgetItem *item, EMacro macro)
{
    QTreeWidgetItem *pressItem = item->child(0);
    QTreeWidgetItem *actionItem = item->child(1);
    QTreeWidgetItem *dataItem = item->child(2);

    item->setText(0, tr("Name: %1").arg(macro.name()));
    pressItem->setText(1, ::textFromPress(macro.press()));
    if (macro.to() == false)
        actionItem->setText(1, tr("Send to Server"));
    else
        actionItem->setText(1, tr("Insert into Input"));

    QString data = macro.data().replace("\n", "\\n");
    dataItem->setText(1, data);
}

void EListPropEditor::createItemForTrigger(ETrigger trigger)
{
    QTreeWidgetItem *topItem = new QTreeWidgetItem;
    topItem->setText(0, tr("Name: %1").arg(trigger.name()));

    QTreeWidgetItem *patternItem = createChildItem(tr("Pattern"),
            trigger.pattern());
    topItem->addChild(patternItem);

    QString patternTypeStr;
    if (trigger.patternSyntax() == QRegExp::RegExp)
        patternTypeStr = tr("Regular Expression");
    else if (trigger.patternSyntax() == QRegExp::Wildcard)
        patternTypeStr = tr("Wildcard");
    else
        patternTypeStr = tr("Plain Text");

    QTreeWidgetItem *patternTypeItem = createChildItem(tr("Pattern Type"),
            patternTypeStr);
    topItem->addChild(patternTypeItem);

    QString checkBoolStr = (trigger.caseSensitivity()) ? "yes" : "no";
    QTreeWidgetItem *caseItem = createChildItem(tr("Check Case"), checkBoolStr);
    topItem->addChild(caseItem);

    QString matchBoolStr = (trigger.options() & TRIG_KEEPGOING) ? "yes" : "no";
    QTreeWidgetItem *continueMatching = createChildItem(tr("Keep Matching"),
            matchBoolStr);
    topItem->addChild(continueMatching);

    QString actionStr;
    if (trigger.options() & TRIG_OMIT)
        actionStr = tr("Omit Text");
    else if (trigger.options() & TRIG_HIGHLIGHT)
        actionStr = tr("Highlight Text");
    else
        actionStr = tr("None");

    QTreeWidgetItem *actionItem = createChildItem(tr("Action"),
            actionStr);
    topItem->addChild(actionItem);

    QString replyStr = trigger.output().replace("\n", "\\n");
    QTreeWidgetItem *replyItem = createChildItem(tr("Reply"), replyStr);
    topItem->addChild(replyItem);

    _managerTree->addTopLevelItem(topItem);
}

void EListPropEditor::updateItemForTrigger(QTreeWidgetItem *item,
        ETrigger trigger)
{
    QTreeWidgetItem *patternItem = item->child(0);
    QTreeWidgetItem *patternTypeItem = item->child(1);
    QTreeWidgetItem *caseItem = item->child(2);
    QTreeWidgetItem *continueMatching = item->child(3);
    QTreeWidgetItem *actionItem = item->child(4);
    QTreeWidgetItem *replyItem = item->child(5);

    patternItem->setText(1, trigger.pattern());

    QString patternTypeStr;
    if (trigger.patternSyntax() == QRegExp::RegExp)
        patternTypeStr = tr("Regular Expression");
    else if (trigger.patternSyntax() == QRegExp::Wildcard)
        patternTypeStr = tr("Wildcard");
    else
        patternTypeStr = tr("Plain Text");
    patternTypeItem->setText(1, patternTypeStr);

    QString checkBoolStr = (trigger.caseSensitivity()) ? "yes" : "no";
    caseItem->setText(1, checkBoolStr);

    QString matchBoolStr = (trigger.options() & TRIG_KEEPGOING) ? "yes" : "no";
    continueMatching->setText(1, matchBoolStr);

    QString actionStr;
    if (trigger.options() & TRIG_OMIT)
        actionStr = tr("Omit Text");
    else if (trigger.options() & TRIG_HIGHLIGHT)
        actionStr = tr("Highlight Text");
    else
        actionStr = tr("None");
    actionItem->setText(1, actionStr);

    replyItem->setText(1, trigger.output().replace("\n", "\\n"));
}

/** Slots for the buttons **/
void EListPropEditor::applyPrefChanges()
{
    if (_propType == EPropAliases)
        _profile->setAliases(_aliases);
    else if (_propType == EPropMacros)
        _profile->setMacros(_macros);
    else if (_propType == EPropTriggers)
        _profile->setTriggers(_triggers);
}

void EListPropEditor::addItem()
{
    if (_propType == EPropAliases) {
        EAliasEditor e(this, true);
        if (e.exec() != QDialog::Accepted)
            return;

        createItemForAlias(e.alias());
        _aliases.append(e.alias());
    }
    else if (_propType == EPropMacros) {
        EMacroEditor e(this, true);
        if (e.exec() != QDialog::Accepted)
            return;

        createItemForMacro(e.macro());
        _macros.append(e.macro());
    }
    else if (_propType == EPropTriggers) {
        ETriggerEditor e(this, true);
        if (e.exec() != QDialog::Accepted)
            return;

        createItemForTrigger(e.trigger());
        _triggers.append(e.trigger());
    }

    if (_managerTree->topLevelItemCount() == 1) {
        setEditingEnabled(true);
        _managerTree->setCurrentItem(_managerTree->topLevelItem(0));
    }
    else if (_managerTree->topLevelItemCount() >= 2)
        updateDirectionsEnabled();
}

void EListPropEditor::deleteItem()
{
    int target = _currentItemIndex;
    baseDeleteItem();

    if (_propType == EPropAliases)
        _aliases.removeAt(target);
    else if (_propType == EPropMacros)
        _macros.removeAt(target);
    else if (_propType == EPropTriggers)
        _triggers.removeAt(target);
}

void EListPropEditor::editItem()
{
    if (_propType == EPropAliases) {
        EAliasEditor e(this, false);
        e.useAlias(_aliases.at(_currentItemIndex));
        if (e.exec() == QDialog::Accepted) {
            updateItemForAlias(_managerTree->currentItem(), e.alias());
            _aliases[_currentItemIndex] = e.alias();
        }
    }
    else if (_propType == EPropMacros) {
        EMacroEditor e(this, false);
        e.useMacro(_macros.at(_currentItemIndex));
        if (e.exec() == QDialog::Accepted) {
            updateItemForMacro(_managerTree->currentItem(), e.macro());
            _macros[_currentItemIndex] = e.macro();
        }
    }
    else if (_propType == EPropTriggers) {
        ETriggerEditor e(this, false);
        e.useTrigger(_triggers.at(_currentItemIndex));
        if (e.exec() == QDialog::Accepted) {
            updateItemForTrigger(_managerTree->currentItem(), e.trigger());
            _triggers[_currentItemIndex] = e.trigger();
        }
    }
}

void EListPropEditor::itemDown()
{
    int target = _currentItemIndex;
    baseMoveItem(1);

    if (_propType == EPropAliases)
        _aliases.swap(target, target + 1);
    else if (_propType == EPropMacros)
        _macros.swap(target, target + 1);
    else if (_propType == EPropTriggers)
        _triggers.swap(target, target + 1);
}

void EListPropEditor::itemUp()
{
    int target = _currentItemIndex;
    baseMoveItem(-1);

    if (_propType == EPropAliases)
        _aliases.swap(target, target - 1);
    else if (_propType == EPropMacros)
        _macros.swap(target, target - 1);
    else if (_propType == EPropTriggers)
        _triggers.swap(target, target - 1);
}

